package com.fly.uaa.exceptionhandler;

import com.fly.uaa.security.exception.UserLockAuthException;
import com.fly.uaa.security.exception.UsernamePasswordAuthException;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.common.DefaultThrowableAnalyzer;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.security.web.util.ThrowableAnalyzer;
import org.springframework.web.HttpRequestMethodNotSupportedException;

import java.io.IOException;

public class CustomWebResponseExceptionTranslator implements WebResponseExceptionTranslator {

    private ThrowableAnalyzer throwableAnalyzer = new DefaultThrowableAnalyzer();

    public CustomWebResponseExceptionTranslator() {
    }
    @Override
    public ResponseEntity<OAuth2Exception> translate(Exception e) throws Exception {
        Throwable[] causeChain = this.throwableAnalyzer.determineCauseChain(e);
        Exception ase = (UsernamePasswordAuthException) this.throwableAnalyzer.getFirstThrowableOfType(UsernamePasswordAuthException.class, causeChain);
        if (ase != null) {
            return this.handleOAuth2Exception(new UsernamePasswordException(ase.getMessage(), ase));
        } else {
            ase = (UserLockAuthException) this.throwableAnalyzer.getFirstThrowableOfType(UserLockAuthException.class, causeChain);
            if (ase != null) {
                return this.handleOAuth2Exception(new UserLockException(ase.getMessage(), ase));
            } else {
                ase = (OAuth2Exception) this.throwableAnalyzer.getFirstThrowableOfType(OAuth2Exception.class, causeChain);
                if (ase != null) {
                    return this.handleOAuth2Exception((OAuth2Exception) ase);
                } else {
                    ase = (AuthenticationException) this.throwableAnalyzer.getFirstThrowableOfType(AuthenticationException.class, causeChain);
                    if (ase != null) {
                        return this.handleOAuth2Exception(new CustomWebResponseExceptionTranslator.UnauthorizedException(e.getMessage(), e));
                    } else {
                        ase = (AccessDeniedException) this.throwableAnalyzer.getFirstThrowableOfType(AccessDeniedException.class, causeChain);
                        if (ase instanceof AccessDeniedException) {
                            return this.handleOAuth2Exception(new CustomWebResponseExceptionTranslator.ForbiddenException(ase.getMessage(), ase));
                        } else {
                            ase = (HttpRequestMethodNotSupportedException) this.throwableAnalyzer.getFirstThrowableOfType(HttpRequestMethodNotSupportedException.class, causeChain);
                            return ase instanceof HttpRequestMethodNotSupportedException ? this.handleOAuth2Exception(new CustomWebResponseExceptionTranslator.MethodNotAllowed(ase.getMessage(), ase)) : this.handleOAuth2Exception(new CustomWebResponseExceptionTranslator.ServerErrorException(e.getMessage(), e));
                        }
                    }
                }
            }
        }
    }

    private ResponseEntity<OAuth2Exception> handleOAuth2Exception(OAuth2Exception e) throws IOException {
        return ResponseEntity
                .status(e.getHttpErrorCode())
                //.status(200)
                .body(new CustomOauth2Exception(e.getOAuth2ErrorCode(), e.getMessage()));
    }

    public void setThrowableAnalyzer(ThrowableAnalyzer throwableAnalyzer) {
        this.throwableAnalyzer = throwableAnalyzer;
    }

    private static class MethodNotAllowed extends OAuth2Exception {
        public MethodNotAllowed(String msg, Throwable t) {
            super(msg, t);
        }

        @Override
        public String getOAuth2ErrorCode() {
            return "method_not_allowed";
        }

        @Override
        public int getHttpErrorCode() {
            return 405;
        }
    }

    private static class UnauthorizedException extends OAuth2Exception {
        public UnauthorizedException(String msg, Throwable t) {
            super(msg, t);
        }

        @Override
        public String getOAuth2ErrorCode() {
            return "unauthorized";
        }

        @Override
        public int getHttpErrorCode() {
            return 401;
        }
    }

    private static class ServerErrorException extends OAuth2Exception {
        public ServerErrorException(String msg, Throwable t) {
            super(msg, t);
        }

        @Override
        public String getOAuth2ErrorCode() {
            return "server_error";
        }

        @Override
        public int getHttpErrorCode() {
            return 500;
        }
    }

    private static class ForbiddenException extends OAuth2Exception {
        public ForbiddenException(String msg, Throwable t) {
            super(msg, t);
        }

        @Override
        public String getOAuth2ErrorCode() {
            return "access_denied";
        }

        @Override
        public int getHttpErrorCode() {
            return 403;
        }
    }

    private static class UsernamePasswordException extends OAuth2Exception {

        public UsernamePasswordException(String msg, Throwable t) {
            super(msg, t);
        }

        @Override
        public String getOAuth2ErrorCode() {
            return "username_password_error";
        }

        @Override
        public int getHttpErrorCode() {
            return 200;
        }
    }

    private static class UserLockException extends OAuth2Exception {

        public UserLockException(String msg, Throwable t) {
            super(msg, t);
        }

        @Override
        public String getOAuth2ErrorCode() {
            return "user_lock_error";
        }

        @Override
        public int getHttpErrorCode() {
            return 200;
        }
    }
}
